#include "unpifi.h"

struct ifi_info *
get_ifi_info(int family, int doaliases) {
	struct ifi_info	*ifi, *ifihead, **ifipnext;
	int				sockfd, len, lastlen, flags, myflags, idx = 0;
	char	*ptr, *buf, lastname[IFNAMSIZ], *cptr;
	struct ifconf	ifc;
	struct ifreq	*ifr, ifrcopy;
	struct sockaddr_in	*sinptr;

	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		err_sys("socket");
	}

	lastlen = 0;
	len = 10 * sizeof (struct ifreq);
	for ( ; ; ) {
		buf = malloc(len);
		ifc.ifc_len = len;
		ifc.ifc_buf = buf;
		if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
			if (errno != EINVAL || lastlen != 0)
				err_sys("ioctl SIOCGIFCONF");
		} else {
			if (ifc.ifc_len == lastlen)
				break;
			lastlen = ifc.ifc_len;
		}
		len = 10 * sizeof (struct ifreq);
		free(buf);
	}

	ifihead = NULL;
	ifipnext = &ifihead;
	lastname[0] = 0;

	for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
		ifr = (struct ifreq *)ptr;
		len = sizeof(struct sockaddr);
		ptr += sizeof(struct ifreq);

		if (ifr->ifr_addr.sa_family != family)
			continue;

		myflags = 0;
		if ((cptr = strchr(ifr->ifr_name, ':')) != NULL) {/*查找':'第一次出现的位置*/
			*cptr = 0;
		}
		if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
			if (doaliases)
				continue;		/*已经处理过此接口*/
			myflags = IFI_ALLAS;
		}
		memcpy(lastname, ifr->ifr_name, IFNAMSIZ);

		ifrcopy = *ifr;
		ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);
		flags = ifrcopy.ifr_flags;
		if ((flags & IFF_UP) == 0)
			continue;		/*忽略关闭的接口*/

		ifi = calloc(1, sizeof(struct ifi_info));
		*ifipnext = ifi;			/*ifipnext 其实链表末尾的结点的next指针*/
		ifipnext = &ifi->ifi_next;	/*链表末尾结点的next指针赋于新结点，即将新结点链接到链表尾*/

		ifi->ifi_flags = flags;
		ifi->ifi_myflags = myflags;

		ioctl(sockfd, SIOCGIFHWADDR, &ifrcopy);
		ifi->ifi_hlen = 6;
		memcpy(ifi->ifi_haddr, &ifrcopy.ifr_hwaddr.sa_data, ifi->ifi_hlen);



		ioctl(sockfd, SIOCGIFMTU, &ifrcopy);
		ifi->ifi_mtu = ifrcopy.ifr_mtu;

		memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
		ifi->ifi_name[IFI_NAME - 1] = '\0';

		ifi->ifi_index = idx;

		sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
		ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in));
		memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));

		if (flags & IFF_BROADCAST) {
			ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
			sinptr = (struct sockaddr_in *)&ifrcopy.ifr_broadaddr;
			ifi->ifi_brdaddr = calloc(1, sizeof(struct sockaddr_in));
			memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
		}

		if (flags & IFF_POINTOPOINT) {
			ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
			sinptr = (struct sockaddr_in *)&ifrcopy.ifr_dstaddr;
			ifi->ifi_dstaddr= calloc(1, sizeof(struct sockaddr_in));
			memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
		}
	}

	free(buf);
	return ifihead;
}

void
free_ifi_info(struct ifi_info *ifihead) {
	struct ifi_info *ifi, *ifinext;

	for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
		if (ifi->ifi_addr != NULL)
			free(ifi->ifi_addr);
		if (ifi->ifi_brdaddr != NULL)
			free(ifi->ifi_brdaddr);
		if (ifi->ifi_dstaddr != NULL)
			free(ifi->ifi_dstaddr);
		ifinext = ifi->ifi_next;
		free(ifi);
	}
}
